/**
 * @author noding
 */
/*
 *	jquery.suggest 1.1 - 2007-08-06
 *
 *	Uses code and techniques from following libraries:
 *	1. http://www.dyve.net/jquery/?autocomplete
 *	2. http://dev.jquery.com/browser/trunk/plugins/interface/iautocompleter.js
 *
 *	All the new stuff written by Peter Vulgaris (www.vulgarisoip.com)
 *	Feel free to do whatever you want with this file
 *	Modified by fishwarter( fishwarter@gmail.com) on 2008.12.29
 *
 */
(function($){
    $.suggest = function(input, options){		
        var $input = $(input).attr("autocomplete", "off");
		var $results = $('#'+options.resultsDiv);				      
        
        var timeout = false; // hold timeout ID for suggestion results to appear	
        var prevLength = 0; // last recorded length of $input.val()
        var cache = []; // cache MRU list
        var cacheSize = 0; // size of cache in chars (bytes?)
        
		function hiddenResults(){
			$results.removeClass(options.showResultsClass);				
			$results.addClass(options.hiddenResultsClass);							
		}
		
		function showResults(){
			$results.removeClass(options.hiddenResultsClass);				
			$results.addClass(options.showResultsClass);	
		}
		
        $input.blur(function(){
            setTimeout(function(){
                hiddenResults();
            }, 200);
        });
       
        $input.keyup(processKey);
        
        function processKey(e){        
            // handling up/down/escape requires results to be visible
            // handling enter/tab requires that AND a result to be selected
            if ((/27$|38$|40$/.test(e.keyCode) && $results.is(':visible')) ||
            (/^13$|^9$/.test(e.keyCode) && getCurrentResult())) {
            
                if (e.preventDefault) 
                    e.preventDefault();
                if (e.stopPropagation) 
                    e.stopPropagation();
                
                e.cancelBubble = true;
                e.returnValue = false;
                
                switch (e.keyCode) {                
                    case 38: // up
                        prevResult();
                        break;                        
                    case 40: // down
                        nextResult();
                        break;                        
                    case 9: // tab
                    case 13: // return
                        selectCurrentResult();
                        break;                        
                    case 27: //	escape
                        hiddenResults();
                        break;                        
                }                
            }
            else 
                if ($input.val().length != prevLength) {                
                    if (timeout) 
                        clearTimeout(timeout);
                    timeout = setTimeout(suggest, options.delay);
                    prevLength = $input.val().length;
                    userInput = $input.val();                    
                }                        
        }
        
		
        function suggest(){        
            var q = $.trim($input.val());            
            if (q.length >= options.minchars) {      
                cached = checkCache(q);                
                if (cached) {                
                    displayItems(cached['items']);                    
                }
                else {         
					$.get(options.source, {q: q}, function(txt) {
						var items = parseTxt(txt, q);
						displayItems(items);
						addToCache(q, items, txt.length);						
					});                  
                }                
            }
            else {            
                hiddenResults();                
            }            
        }    
		function parseTxt(txt, q) {
			var items = [];
			var tokens = txt.split(options.delimiter);
			
			// parse returned data for non-empty items
			for (var i = 0; i < tokens.length; i++) {
				var token = $.trim(tokens[i]);
				if (token) {

					token = token.replace(
						new RegExp(q, 'ig'), 
						function(q) { return '<span class="' + options.matchClass + '">' + q + '</span>' }
						);
	
					items[items.length] = token;
				}
			}
			
			return items;
		}

        function checkCache(q){   
            for (var i = 0; i < cache.length; i++) 
                if (cache[i]['q'] == q) {
                    cache.unshift(cache.splice(i, 1)[0]);
                    return cache[0];
                }            
            return false;            
        }
        
        function addToCache(q, items, size){    
            while (cache.length && (cacheSize + size > options.maxCacheSize)) {
                var cached = cache.pop();
                cacheSize -= cached['size'];
            }            
            cache.push({
                q: q,
                size: size,
                items: items
            });            
            cacheSize += size;            
        }
        
        function displayItems(items){ // 显示搜索结果, added by hzx      
            if (!items) 
                return;            
            if (!items.length) {
                hiddenResults();
                return;
            }            
            var html = '';
			for(var i=0;i<items.length;i++){
				html+=("<li>"+items[i]+"</li>");	
			};
            $results.html(html);
			showResults();
				
            $results.children('li').mouseover(function(){
                $results.children('li').removeClass(options.selectClass);
                $(this).addClass(options.selectClass);
            }).click(function(e){
                e.preventDefault();
                e.stopPropagation();
                selectCurrentResult();
            });           
        }
        
		function getCurrentResult() {
		
			if (!$results.is(':visible'))
				return false;
		
			var $currentResult = $results.children('li.' + options.selectClass);
			
			if (!$currentResult.length)
				$currentResult = false;
				
			return $currentResult;

		}
		
		function selectCurrentResult() {
		
			$currentResult = getCurrentResult();
		
			if ($currentResult) {
				$input.val($currentResult.text());
				hiddenResults();
				
				if (options.onSelect)
					options.onSelect.apply($input[0]);
					
			}
		
		}
        
        function nextResult(){        
            $currentResult = getCurrentResult();            
            if ($currentResult) {
                $currentResult.removeClass(options.selectClass).next().addClass(options.selectClass)
                $input.val($("."+options.suggestWordClass,$currentResult.next()).text() || userInput);
            }
            else {
				$currentResult = $results.children('div:first-child');
                $currentResult.addClass(options.selectClass);
                $input.val($("."+options.suggestWordClass,$currentResult).text());
            }
        }
		        
        function prevResult(){        		       
            $currentResult = getCurrentResult();
            
            if ($currentResult) {
				$currentResult.removeClass(options.selectClass).prev().addClass(options.selectClass);
                $input.val($("."+options.suggestWordClass,$currentResult.prev()).text() || userInput);
            }
            else {
				$currentResult = $results.children('div:last-child');
				$currentResult.addClass(options.selectClass);
	            $input.val($("."+options.suggestWordClass,$currentResult).text());
            }
        }        
    }
    
    $.fn.suggest = function(source,options){
        if (!source) 
            return;
        
		// 这种写法相当不错啊，python里面可以这样子的吗？
        options = options ||
        {};
        options.source = source;
        options.delay = options.delay || 100;
		
		options.resultsDiv = options.resultsDiv || 'suggest_result';
        options.showResultsClass = options.showResultsClass || 'show_suggest_results';
		options.hiddenResultsClass = options.hiddenResultsClass || 'hidden_suggest_results';
        options.selectClass = options.selectClass || 'suggest_word_selected';
		options.suggestWordClass = options.suggestWordClass || 'suggest_word';
		options.suggestAttachClass = options.suggestAttachClass || 'suggest_attach';
		options.suggestFlagClass = options.suggestFlagClass || 'suggest_flag';
		options.statKey = options.statKey || 'keyfrom';
		options.matchClass = options.matchClass || 'ac_match';
        options.minchars = options.minchars || 1;
        options.delimiter = options.delimiter || '|';
        options.onSelect = options.onSelect || false;
        options.maxCacheSize = options.maxCacheSize || 65536;
        
        this.each(function(){
            new $.suggest(this, options);
        });
        
        return this;        
    };    
})(jQuery);
